// Copyright (C) 2010 and onwards Harik Shazeer Labs
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors: Noam Shazeer and Georges Harik

#include "objects.h"

map<string, Object *> STRING_OBJECT;
map<Object *, string> OBJECT_STRING;

void CheckObjectSizes() {
  CHECK(sizeof(Object *) == 8);
  CHECK(sizeof(void *) == 8);
  CHECK(sizeof(ObjectType) == 4);
  CHECK(sizeof(int32) == 4);
  CHECK(sizeof(int64) == 8);
  CHECK(sizeof(float) == 4);
}

void Object::ComputeNodeCount() {
  CHECK(GetType() == ARRAY_TYPE);
  node_count_ = 1;
  for (uint64 i=0; i<ArraySize(); i++) {
    uint64 cnc = NodeCount(Ref(i));
    node_count_ += cnc;
    if ( (cnc == 0) || (node_count_ >= (1ull << 60)) ) {
      node_count_ = 0; return;
    }
  }
}


bool IsString(Object *p) {
  if (GetType(p) != ARRAY_TYPE) return false;
  for (uint64 i=0; i<p->ArraySize(); i++) 
    if (GetType(p->Ref(i)) != CHAR) return false;
  return true;
}

string StringToCPPString(Object *p) {
  if (!IsString(p)) return "";
  string ret = "";
  for (uint64 i=0; i<p->ArraySize(); i++) {
    ret += GetChar(p->Ref(i));
  }
  return ret;
}

bool DepthLessThan(Object *p, int64 max_depth) {
  int64 nc = NodeCount(p);
  if (nc && (nc < max_depth)) return true;
  if (max_depth < 0) return false;
  if (GetType(p) != ARRAY_TYPE) return true;
  for (uint64 i=0; i<p->ArraySize(); i++) {
    if (!(DepthLessThan(p->Ref(i), max_depth-1))) return false;
  }
  return true;
}

void AllSubnodes(Object *p, vector<Object *> *result) {
  result->push_back(p);
  if (GetType(p) != ARRAY_TYPE) return;
  for (uint64 i=0; i<p->ArraySize(); i++) AllSubnodes(p->Ref(i), result);
}

Object * MakeSymbol(string name) {
  uint64 hv = Fingerprint(name);
  Object * ret = (Object *) ((hv << 3) + SYMBOL);
  if (STRING_OBJECT % name) {
    cout << "Redefining symbol: " << name << endl;
    CHECK(false);
  }
  STRING_OBJECT[name] = ret;
  OBJECT_STRING[ret] = name;
  return ret;
}

Object * MakeConstant(string name) {
  static int next = 0;
  return AddEnum(CONSTANT, next++, name);
}

Object * AddEnum(ObjectType t, int value, string name) {
  Object * o = MakeEnum(t, value);
  CHECK(!(STRING_OBJECT % name));
  STRING_OBJECT[name] = o;
  OBJECT_STRING[o] = name;
  return o;
}

Object * GetObjectOrMakeSymbol(string name){
  Object ** ret = (STRING_OBJECT % name);
  if (ret) return *ret;
  return MakeSymbol(name);
}

